/*  
  -- The Lime II Project -- 

  A tuplespaces-based middleware for coordinating agents and mobile hosts.
  Copyright (C) 2005.
  Gian Pietro Picco, Amy L. Murphy, Lorenzo Bellini.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received this copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
*/

package lime2;
import java.net.*;
import java.io.Serializable;

/**
 * Models a location. Basically it enriches LimeServerID with the notion of wildcard.
 * 
 * @author Gian Pietro Picco
 * @author Amy Murphy
 * @author Lorenzo Bellini
 */

public class Location implements HashID, Serializable
{
 /**
	 * 
	 */
	private static final long serialVersionUID = -1175491088846890659L;
// private constants (wildcards)	
 private final static byte _INVALID     = 0;
 private final static byte _UNSPECIFIED = 1;		
 private final static byte _NORMAL      = 2;
 
 // private data members	
 private LimeServerID id;
 private byte wildcard;	

 /**
  * Constant: INVALID Location. 
  */	
 public final static Location INVALID = new Location(_INVALID);
 
 /**
  * Constant: UNSPECIFIED Location. 
  */	
 public final static Location UNSPECIFIED = new Location(_UNSPECIFIED);
  
 /**
  * Constant: HERE Location (= this host). 
  * <br>
  * Please note that for HERE to be valid, the LimeServer of this JVM must have been booted. If the LimeServer has not
  * been booted the notion of HERE has no meaning (i.e, ..which unicast port should we refer to ?) 
  * HERE is initialized by the package-only visible method "initHere": this method is invoked by the LimeServer during
  * booting. The object-id of a final object cannot be re-assigned after the first initialization, but the "content" of
  * the object can be modified. 
  * <br>
  * If you need to refer to "this" location before the LimeServer has been booted, you have to know in advance the 
  * unicast port that will be used for the Lime server (service); then to create the Location instance, call
  * <code>build(localhost, u_port)</code>.
  * <br>
  * Please note that when an agent is loaded, it is guaranteed that the LimeServer has already been booted: if you 
  * write an agent you don't need to worry about HERE.
  * <br>
  * <code>Location.HERE.getID()</code> is exactly the same as <code>LimeServer.getServer().getServerID()</code>
  */
 public final static Location HERE = new Location(null); 
  
 /**
  * Constructor method.
  * 
  * @param id the LimeServerID  
  */			
 public Location(LimeServerID id) 
 {
  this.wildcard = _NORMAL;
  this.id = id;
 }
 
 // private constructor: requires one of the private wildcard as parameters 
 private Location(byte wildcard) 
 {
  this.wildcard = wildcard;
  this.id = null;
 } 
  
 /**
  * Creates tentatively a Location: if it is not possible to resolve the given host, a LimeRuntimeException is thrown.
  * If you use IP addresses (with a valid format) and not DNS names, you'll be ok.
  * 
  * @param host name/IP of the target host
  * @param port port number
  * @return corresponding Location
  * @throws InvalidLocationException
  */
 public static Location build(String host, int port)
 {
  InetAddress i = null;	
  try { i = InetAddress.getByName(host); }
  catch (UnknownHostException uhe) { throw new InvalidLocationException("can't resolve DNS name or invalid IP format", uhe); }
  return new Location(new LimeServerID(i, port));     
 }
 
 /**
  * Tests whether this Location is used as a wildcard.
  * 
  * @return <code>boolean</code>
  */ 
 public boolean isWildCard() 
 { 
  return (wildcard != _NORMAL); 
 }

 /**
  * Tests whether this Location is wildcard INVALID.
  * 
  * @return <code>boolean</code>
  */ 
 public boolean isInvalid() 
 { 
  return (wildcard == _INVALID); 
 } 
  
 /**
  * Tests whether this Location is wildcard UNSPECIFIED.
  * 
  * @return <code>boolean</code>
  */ 
 public boolean isUnspecified() 
 { 
  return (wildcard == _UNSPECIFIED); 
 } 

 /**
  * Tests whether this Location is "this host".
  *
  * @return <code>boolean</code>
  */
 public boolean isHere()
 {
  return this.equals(Location.HERE);	
 }
 
 /**
  * Returns the LimeServerID corresponding to this location.
  * 
  * @return LimeServerID corresponding to this location 
  */
 public LimeServerID getID() 
 { 
  return id; 
 }

 /**
  * Returns the hashcode.
  * 
  * @return the hashcode
  */ 
 public int hashCode() 
 {
  return ((id == null ? new Integer(0) : new Integer(id.hashCode())).toString() + new Byte(wildcard).toString()).hashCode();
 }
 
 /**
  * Tests for equality.
  * 
  * @param l Object
  * @return <code>boolean</code> 
  */
 public boolean equals(Object l) 
 {
  boolean result = false;
  if ((l != null) && (l instanceof Location)) 
     {
	  if (isWildCard()) result = (wildcard == ((Location) l).wildcard); 
	  else {
		  if(id == null)
			  result = ((Location)l).id == null;
		  else
			  result = id.equals(((Location) l).id);
	  }
	 }
  return result;
 }

 /**
  * toString method.
  * 
  * @return String representing this Location
  */
 public String toString() 
 {
  String ret = null;
  if (isWildCard()) { 
	  if (isUnspecified()) ret = "unspecified"; 
	  else if(isInvalid()) ret = "invalid";
	  else if(isHere()) ret = "here";}
  else ret = (id == null ? "null" : id.toString());
  return ret;
 }

 /**
  * This package-only visible method is invoked by the LimeServer during booting; it assigns to HERE a valid value. 
  * 
  * @param id the LimeServerID for this host
  */
 static void initHere(LimeServerID id)
 {
  HERE.id = id;	
 }
 
 /**
  * This package-only visible method is invoked by the LimeServer during booting; it assigns to HERE a valid value. 
  * !!Only for test
  * @param id the LimeServerID for this host
  */
 public static void initHereForTest(LimeServerID id)
 {
  HERE.id = id;	
 } 
 
 
 /**
  * Return the size occupied by this object
  * @return
  * @author Xing Jiankuan
  */
 public int getSize() {
	 //	LimeServerID
	 //		host InetAddress
	 //			family int 4
	 //			address int 4
	 //			hostname String length * 2
	 //		port int 4
	 //	wildcard byte 1
	 return 13 + id.getHost().getHostName().length() * 2;
 }
 
}